package com.example.musicplayer.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.example.musicplayer.dto.Song;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;

/**
 * Class that finds all music whether on SD card or Internal Memory
 * @author Jonathan
 */
public class MusicHandlerStub implements IMusicHandler {
	
	//this will be used to create a preference file
	public static final String PREFS_NAME = "MUSIC_PLAYBACK_DATA";
	//A List that holds song objects, MASTER SONG LIST that prevents from unnecessary querying
	List<Song> songList = new ArrayList<Song>();
	//A custom playlist that holds user picked songs (later intergration)
	List<Song> playList = new ArrayList<Song>();
	//list of random songs
	List<Song> shuffledList = new ArrayList<Song>();
	//URI for media source
	private Uri mediaUri;
	private Uri albumUri;
	private int searchIndex = 0;
	ContentResolver contentResolver;
	//handles shuffle function
	boolean isShuffled;
	
	public MusicHandlerStub(ContentResolver cr, boolean shuffled) {
		//sets shuffled state
		isShuffled = shuffled;
		contentResolver = cr;
		//gets called to populate musiclist cursor
		RetrieveMusic();
    }
	public Cursor cursorHandler(Uri mediaUri,String [] columns ,String selection) {
		return null;
	}
	
	/**
	 * Method that only finds music on device using cursors, then calls addSongs
	 * 
	 * @return a cursor object containing data about music 
	 */
	@Override
	public void RetrieveMusic() {
		if(MediaStateChecker()){
			Log.i("SD State", "SD card found. Now using SD card.");
			mediaUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
		}
		else{
			Log.i("SD State", "No SD card detected. Now using internal storage");
			mediaUri = android.provider.MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
		}
		//selection argument that help define what music is selected
		String[] selectionArgs = null;
		//an array that tells the curser of what data about the music to bring back such as data path/name/song artist.
		String [] musicCharacteristics = {
			MediaStore.Audio.Media._ID,
			MediaStore.Audio.Media.DATA,
			MediaStore.Audio.Media.TITLE,
			MediaStore.Audio.Media.ALBUM,
			MediaStore.Audio.Media.ALBUM_ID,
			MediaStore.Audio.Media.ARTIST,
			MediaStore.Audio.Media.DURATION
			};
		//loads cursor with music data from uri source.
		Cursor musicCursor = contentResolver.query(mediaUri, musicCharacteristics, MediaStore.Audio.Media.IS_MUSIC + " = 1", selectionArgs, null);
		if(musicCursor == null){
			Log.e("Empty Cursor", "Cursor was not loaded. Error occured!");
			return;
		}
		if(!musicCursor.moveToFirst())
		{
		 Log.e("Empty", "No music found on SD/Card");
		 return;
		}
		else{
			//only adds music to songlist is there has been new music added to device
			if(songList.size() < musicCursor.getCount ())
			{
				musicCursor.moveToPosition(songList.size());
				//call to addsong method
				AddSongs(musicCursor);
				musicCursor.close();
			}
       }
	}
	
	/**
	 * Adds all songs from cursor into song objects and loads them into songlist
	 * 
	 * @param musicCursor, cursor that contains music metadata and adds to master songlist
	 * 
	 * @return songlist, collections of song objects
	 */
	public void AddSongs(Cursor musicCursor) {
		do{
			Song song = new Song();
			long album_id = musicCursor.getLong(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
			song.setSongAlbum(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)));
			song.setSongArtist(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
			song.setSongName(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE)));
			song.setSongPath(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
			song.setSongDuration(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
			songList.add(song);
		}while(musicCursor.moveToNext());
				String tag = null;
				//igonore this line for  debugging
				Log.i(tag,Integer.toString(songList.size()));
				if(isShuffled == true)
				Shuffler();
	}
	
	/**
	 * Returns a content resolver object that is used to intantiate Content Resolver in retrieve method
	 * 
	 * @return ContentReolver
	 */
	@SuppressWarnings("unused")
	private ContentResolver getContentResolver() {
		return contentResolver;
	}
	
	/** 
	 * Retrieves album art using the album ID
	 * 
	 * @params long albumID, the unique album ID provided my the content provider
	 * 
	 * @return returns albumart filepath in a string.
	 */
	@Override
	public String RetrieveAlbumArt(long albumID) {
		//(imcomplete) will set default blank album image if none exist with song
		String defaultImage = "res://raw/albumplaceholder";
		if(MediaStateChecker()){
			albumUri = android.provider.MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
		}
		else{
			albumUri = android.provider.MediaStore.Audio.Albums.INTERNAL_CONTENT_URI;
		}
		//a string that limits the query to only return album arts matching the album ID
		String selection = MediaStore.Audio.Albums._ID + "=" + albumID;
		
		//only brings baclk one column opposed to all columns, more effiecent
		String [] albumcharacteristics = {
				MediaStore.Audio.Albums.ALBUM_ART
		};
		Cursor albumArtCursor = contentResolver.query(albumUri,albumcharacteristics,selection,null,null);
		albumArtCursor.moveToPosition(0);
		if(!albumArtCursor.isNull(albumArtCursor.getColumnIndex("album_art"))){
		 //a string that holds the album art file path
		String artUri = albumArtCursor.getString(albumArtCursor
				.getColumnIndex("album_art"));
		albumArtCursor.close();
			return artUri;
		}else
			albumArtCursor.close();
			return defaultImage;
	}
	
	/**
	 * Checks whether there is a External SD card inserted
	 * 
	 * @return returns a true or false based on media state
	 */
	@Override
	public boolean MediaStateChecker() {
		//gets external media state (either media_mounted or media_mounted)
		String mediaState = Environment.getExternalStorageState();
		//checks whether or not a external SD is attached by comparing current external storage state.
		//and returns true if attached.
		if(Environment.MEDIA_MOUNTED.equals(mediaState)){
			return true;
		}
		//returns false if no SD card present or error with SD card
		else
		return false;
	}
	
	/**
	 * Method that gets a saved list of songs objects to be used to play music
	 * 
	 * @return List, list of songs either shuffled or normal sort order
	 */
	@Override
	public List<Song> GetMusicList(boolean isshuffled1) {
		if(isShuffled == false)
		return songList;
		else
		return shuffledList;
	}
	/**
	 * Method that saves the last song played and position of song when app closed
	 * 
	 * @param songIndex, playpostion, the index in the songlist of the last played song 
	 * and the songs play played position. SO if a song played 30 seconds when the app is closed
	 * and reopened you can play from where you left off.
	 * 
	 */
	@Override
	public void SaveSongList(List<Song> list, String tableName) {
		// TODO Auto-generated method stub
		
	}
	
	
	/**
	 * Method that searches for a song recursively based off user text
	 * 
	 * @param songSearchData
	 * 
	 * @return searchedSong, instances of searched song if found otherwise returns null
	 */
	@Override
	public Song SongSearch(String songSearchData){
		Song searchedSong;
		//retrieves first song from songList
		searchedSong = songList.get(searchIndex);
		//checks whether or not the string exist in this string instance and returns it
		if(songSearchData.equalsIgnoreCase(searchedSong.getSongName()) || 
				songSearchData.equalsIgnoreCase(searchedSong.getSongArtist()) 
				|| songSearchData.equalsIgnoreCase(searchedSong.getSongAlbum()))
		{
		return searchedSong;
		}
		//checkes if the songIndex is larger than songList size to determine whether or not to recurse
		else if(searchIndex < songList.size()-1)
		{
		searchIndex++;
		SongSearch(songSearchData);
		}
		//returns null if the string was found in no each instances of songs in the songlist
		return null;
	}
	
	/**
	 * Method that handles making a custom playlist
	 * 
	 * @param Song, user selected song that's to be added to playlist
	 */
	@Override
	public void CreatePlayList(Song song) {
		playList.add(song);
	}
	
	/**
	 * Method that returns custom playlist
	 * 
	 * @return playlist, custom List<Song> of user selected songs
	 */
	public List<Song> GetPlayList(){
		//if playlist is not empty returns the List
		if(!playList.isEmpty())
		return playList;
		//if playlist is empty returns null, which has to be handled in activity
		else
		return null;
	}
	
	/**
	 * Method that generates a random list of songs, ensures that no so is entered twice
	 * 
	 * @return shuffledList, a list of randomly choosen songs
	 */
	public List<Song> Shuffler(){
		/*an array of ints that were randomly generated. 
		  This array will be used to compare new generated numbers a
		  and make sure the same song wont be added
		 */
		ArrayList<Integer> shuffleIndex = new ArrayList<Integer>();
		Random rn = new Random();
		//variable to hold random numbers
		int randomNumber;
		/*adds randomly selected songs to list for as long
		 * as the number of randomly generated numbers, inside of the arraylist
		 * is smaller than the size of the master songList 		 * 
		 */
		do{
			do{
				//generates random number based off size of master song list or could throughs error
				randomNumber = rn.nextInt(songList.size() + 1 - 1);
			}while(shuffleIndex.contains(randomNumber));
				//adds random number to array list outside of do/while loop inorder to stop infinite loop
				shuffleIndex.add(randomNumber);
				//then adds the random song into shuffledList
				shuffledList.add(songList.get(randomNumber));
				//this statement repeat based off numbers in array compared to size of songList
		}while(shuffleIndex.size() < songList.size());
		//igonore this line for  debugging
		Log.i("Count",Integer.toString(songList.size()));
		return shuffledList;
	}
	
}
